package com.founder.gateway.component.transport.netty.extension.support;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.founder.gateway.system.util.ExpressionEvaluator;
import org.apache.commons.lang3.StringUtils;
import org.setu.framework.util.SetuApplicationUtil;
import org.setu.framework.utils.DateTimeHelper;
import org.springframework.core.NestedExceptionUtils;

import com.founder.gateway.component.transport.netty.exception.NettyException;
import com.founder.gateway.component.transport.netty.filter.NettyFilterChain;
import com.founder.gateway.component.transport.netty.http.NettyHttpRequest;
import com.founder.gateway.component.transport.netty.http.NettyHttpResponse;
import com.founder.gateway.oauth.service.OrgService;
import com.founder.gateway.orm.fzapi.bean.entity.ApiParamEntity;
import com.founder.gateway.service.ApiService;
import com.founder.gateway.system.bean.SetuSession;
import com.founder.gateway.system.consts.ApiSetuErrorCode;
import com.founder.gateway.system.consts.Consts;
import com.founder.gateway.system.util.HttpNormUtil;
import com.founder.gateway.system.util.ParamsVerifyUtil;

import io.netty.handler.codec.http.HttpHeaderNames;

public class RequestParamNettyHttpFilter extends AbstractNettyHttpFilter {
    private ExpressionEvaluator evaluator = new ExpressionEvaluator();

    @Override
    public void doFilter(NettyHttpRequest request, NettyHttpResponse response, NettyFilterChain chain) throws IOException, NettyException {
        doRequestParam(request, response, chain);
        chain.doFilter(request, response);
    }

    private void doRequestParam(NettyHttpRequest request, NettyHttpResponse response, NettyFilterChain chain) throws NettyException {
        Map<String, Object> context = new HashMap<>();
        SetuSession session = getSession();
        context.put("request", request);
        context.put("response", response);
        context.put("session", session);
        context.put("params", request.getParameterMap().getMapsSingle());
        context.put("utils", ParamsVerifyUtil.INSTANCE);
        context.put("today", DateTimeHelper.formatDatetoString(new Date()));
        context.put("now", DateTimeHelper.formatDateTimetoString(new Date()));

        List<ApiParamEntity> VERIFY_GXDW_LIST = new LinkedList<ApiParamEntity>();//等待管辖校验的列表

        for (ApiParamEntity entity : SetuApplicationUtil.getBean(ApiService.class).queryApiParam(chain.getApiSchemeEntity().getId())) {
            String name = entity.getParaName();
            String type = entity.getParamsType();
            // 缺少必要配置
            if (name == null || type == null) {
                continue;
            }
            List<String> values = new ArrayList<>();
            // 提取参数
            switch (type) {
                // http header
                case Consts.REQUEST_HEADERS: {
                    List<String> list = request.headers().getAll(name);
                    if (list != null) {
                        values.addAll(list);
                    }
                    break;
                }
                // 混合参数，参数可以随意放在 param 或 body 中
                case Consts.REQUEST_MIX_PARAMS:
                case Consts.REQUEST_MIX_BODY:
                    // url param
                case Consts.REQUEST_PARAMS: {
                    List<String> list = request.getParameterMap().getParam().get(name);
                    if (list != null) {
                        values.addAll(list);
                    }
                    if (Consts.REQUEST_PARAMS.equals(type)) {
                        break;
                    }
                }
                // http body
                case Consts.REQUEST_BODY: {
                    List<String> list = request.getParameterMap().getBody().get(name);
                    if (list != null) {
                        values.addAll(list);
                    }
                    if (Consts.REQUEST_BODY.equals(type)) {
                        break;
                    }
                    // 移除原先的参数
                    request.getParameterMap().getParam().remove(name);
                    request.getParameterMap().getBody().remove(name);
                    // 添加到目标位置
                    if (Consts.REQUEST_MIX_PARAMS.equals(type)) {
                        request.getParameterMap().addParam(name, values);
                    } else {
                        request.getParameterMap().addBody(name, values);
                    }
                    break;
                }
                // user session 
                case Consts.REQUEST_SESSION_HEADERS:
                case Consts.REQUEST_SESSION_PARAMS:
                case Consts.REQUEST_SESSION_BODY:
                    if (session != null) {
                        Object o = session.getAttribute(name);
                        if (o != null) {
                            String value = o.toString();
                            values.add(value);
                            if (Consts.REQUEST_SESSION_PARAMS.equals(type)) {
                                request.getParameterMap().addParam(name, value);
                            } else if (Consts.REQUEST_SESSION_BODY.equals(type)) {
                                request.getParameterMap().addBody(name, value);
                            } else {
                                request.headers().add(name, value);
                            }
                        }
                    }
                    break;
            }
            // 必填校验
            boolean isRequired = Consts.REQUEST_PARAM_REQUIRED.equals(entity.getRequired());
            if (isRequired && HttpNormUtil.isAnyBlank(values)) {
                throw new NettyException(ApiSetuErrorCode.API_REQUEST_PARAM_VERIFY, name + HttpNormUtil.NOT_BLANK);
            }
            String verifyType = entity.getVerifyType();
            String verifyValue = entity.getVerifyValue();
            String verifyErrorInfo = entity.getVerifyErrorInfo();
            // 默认值填充
            boolean append = Consts.REQUEST_PARAM_VERIFY_APPEND_PARAM.equals(verifyType);
            if (append || Consts.REQUEST_PARAM_VERIFY_DEFAULT.equals(verifyType)
                    || (Consts.REQUEST_PARAM_VERIFY_IF_NULL_FOR_DEFAULT.equals(verifyType) && HttpNormUtil.isAnyBlank(values))) {
                setRequestParam(request, response, name, verifyValue, type, append);
            }
            // 正则校验
            else if (Consts.REQUEST_PARAM_VERIFY_REGEX.equals(verifyType)) {
                for (String value : values) {
                    if (value != null && !value.matches(verifyValue)) {
                        throw new NettyException(ApiSetuErrorCode.API_REQUEST_PARAM_VERIFY, verifyErrorInfo);
                    }
                }
            }
            // EL表达式校验
            else if (Consts.REQUEST_PARAM_VERIFY_EL.equals(verifyType)) {
                for (String value : values) {
                    context.put("value", value);
                    boolean success;
                    try {
                        success = evaluator.evaluateBoolean(verifyValue, context);
                    } catch (Exception e) {
                        Throwable throwable = NestedExceptionUtils.getMostSpecificCause(e);
                        throw new NettyException(StringUtils.isNotBlank(throwable.getMessage()) ? throwable.getMessage() : e.getMessage());
                    }
                    if (!success) {
                        throw new NettyException(ApiSetuErrorCode.API_REQUEST_PARAM_VERIFY, verifyErrorInfo);
                    }
                }
            }
            // EL处理
            else if (Consts.REQUEST_PARAM_VERIFY_EL_HANDLE.equals(verifyType)) {
                for (String value : values) {
                    context.put("value", value);
                    try {
                        String result = evaluator.evaluateString(verifyValue, context);
                        setRequestParam(request, response, name, result, type, false);
                    } catch (Exception e) {
                        Throwable throwable = NestedExceptionUtils.getMostSpecificCause(e);
                        throw new NettyException(ApiSetuErrorCode.API_REQUEST_PARAM_HANDLE, StringUtils.isNotBlank(throwable.getMessage()) ? throwable.getMessage() : e.getMessage());
                    }
                }
            }
            // 管辖校验，要在最后执行，防止配置的最大权限没有填充进去
            else if (Consts.REQUEST_PARAM_VERIFY_GXDW.equals(verifyType)) {
                VERIFY_GXDW_LIST.add(entity);
            }
            // 别名
            String alias = entity.getParaAlias();
            if (StringUtils.isNotBlank(alias)) {
                switch (type) {
                    case Consts.REQUEST_SESSION_HEADERS:
                    case Consts.REQUEST_HEADERS: {
                        List<String> list = request.headers().getAll(name);
                        if (list != null) {
                            request.headers().remove(name);
                            request.headers().add(alias, list);
                        }
                        break;
                    }
                    case Consts.REQUEST_SESSION_PARAMS:
                    case Consts.REQUEST_MIX_PARAMS:
                    case Consts.REQUEST_PARAMS: {
                        List<String> list = request.getParameterMap().getParam().remove(name);
                        if (list != null) {
                            request.getParameterMap().getParam().put(alias, list);
                        }
                        break;
                    }
                    case Consts.REQUEST_SESSION_BODY:
                    case Consts.REQUEST_MIX_BODY:
                    case Consts.REQUEST_BODY: {
                        List<String> list = request.getParameterMap().getBody().remove(name);
                        if (list != null) {
                            request.getParameterMap().getParam().put(alias, list);
                        }
                        break;
                    }
                }
            }
        }
        verifyGxdw(VERIFY_GXDW_LIST, request);
    }

    private static void setRequestParam(NettyHttpRequest request, NettyHttpResponse response,
                                        String name, String value, String type, boolean append) {
        switch (type) {
            case Consts.REQUEST_SESSION_HEADERS:
            case Consts.REQUEST_HEADERS:
                if (value == null) {
                    request.headers().remove(name);
                } else {
                    if (append) {
                        request.headers().add(name, value);
                    } else {
                        request.headers().set(name, value);
                    }
                    if (name.equals("content-type")) {
                        response.headers().set(HttpHeaderNames.CONTENT_TYPE, value);
                    }
                }
                break;
            case Consts.REQUEST_SESSION_PARAMS:
            case Consts.REQUEST_MIX_PARAMS:
            case Consts.REQUEST_PARAMS:
                if (value == null) {
                    request.getParameterMap().getParam().remove(name);
                } else if (append) {
                    request.getParameterMap().addParam(name, value);
                } else {
                    request.getParameterMap().setParam(name, value);
                }
                break;
            case Consts.REQUEST_SESSION_BODY:
            case Consts.REQUEST_MIX_BODY:
            case Consts.REQUEST_BODY:
                if (value == null) {
                    request.getParameterMap().getBody().remove(name);
                } else if (append) {
                    request.getParameterMap().addBody(name, value);
                } else {
                    request.getParameterMap().setBody(name, value);
                }
                break;
        }
    }


    private void verifyGxdw(List<ApiParamEntity> VERIFY_GXDW_LIST, NettyHttpRequest request) throws NettyException {
        //参数处理完毕，开始校验管辖
        for (ApiParamEntity entity : VERIFY_GXDW_LIST) {
            String name = entity.getParaName();

            List<String> valueList = request.getParameterMap().getParam().get(name);
            if (valueList != null && !valueList.isEmpty()) {//有传入管辖字段，则要校验管辖
                String maxGxzdmc = entity.getVerifyValue();
                List<String> maxGxdwList = request.getParameterMap().getParam().get(maxGxzdmc);
                if (maxGxdwList == null || maxGxdwList.isEmpty()) {
                    throw new NettyException(ApiSetuErrorCode.API_REQUEST_GXDW_VERIFY, "未获取到默认的管辖配置");
                }

                for (String gxdw : valueList) {
                    boolean isInGx = false;//是否在管辖范围内
                    for (String maxGxdw : maxGxdwList) {
                        String gxdwZdmc = SetuApplicationUtil.getBean(OrgService.class).getGxdwZdmc(maxGxdw, gxdw);
                        if (gxdwZdmc != null) {
                            //gxdw是maxGxdw的下级单位或者本身
                            addGxdw(request, gxdwZdmc, gxdw);
                            isInGx = true;
                            break;
                        }
                    }
                    if (!isInGx) {//不在管辖范围内
                        throw new NettyException(ApiSetuErrorCode.API_REQUEST_GXDW_VERIFY, gxdw + "超出管辖范围");
                    }
                }


            }
        }
    }

    /**
     * @param @param request
     * @param @param gxdwZdmc
     * @param @param gxdw    设定文件
     * @return void    返回类型
     * @Title: addGxdw
     * @Description: 添加管辖单位参数
     * @throw
     */
    private void addGxdw(NettyHttpRequest request, String gxdwZdmc, String gxdw) {
        List<String> valueList = request.getParameterMap().getParam().get(gxdwZdmc);
        if (valueList == null) {
            valueList = new LinkedList<String>();
        }

        if (!valueList.contains(gxdw)) {
            valueList.add(gxdw);
        }
        request.getParameterMap().getParam().put(gxdwZdmc, valueList);
    }
}
